package chat.fullduplex;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/** 
 * This is an evolution of the EchoServer in the <code>chat</code> package that lets the client
 * and server exchange messages freely and in full-duplex (look it up) mode. The previous EchoServer
 * operated in half-duplex mode : Local and remote communication was synchronized and alternated
 * from one to the other.
 * 
 * In the following I refactor the original chat program, transforming the single read loop that
 * synchronizes reading from keyboard and remote client into two separate read loops (read from 
 * the remote client and read from the local keyboard) that execute in  separate threads. This 
 * allows them to behave asynchronously, Ie read from client and keyboard in any order and
 * at the same time (full-duplex).
 * 
 * @see chat.EchoServer for original half duplex implementation
 * @author Lom Hillah
 * @author Justin Templemore
 *
 */
public class EchoServer implements LocalChatEndPoint
{
	// accepts incoming TCP connections
	private ServerSocket serverSocket = null;
	// connection to single client
	private Socket clientSocket = null;
	// flag to show if we have quit or not
	private boolean isActive = true;
	
	/**
	 * The main has been rewritten to create an object that implements the client
	 * logic. This is good practice : Main should do nothing, only objects should work.
	 * Trust me, it avoids lots of problems (notably related to the static context) later on.
	 */
	public static void main (String[] args)
	{
		new EchoServer().go();
		// Have a look at when this message is displayed.
		// Remember : In Java, the program finishes when the main thread AND all non daemon threads
		// have terminated.
		System.out.println("Finished server main thread");
	}
	
	/**
	 * Server logic of the simple chat program.
	 */
	public void go() 
	{
		try 
		{
			// display local host information (very useful for setting up a conxn from the client)
			InetAddress localhost = InetAddress.getLocalHost();
			System.out.println ("Local host  is " + localhost);
			// open a server socket that listens for socket connections (clients) on port 4444
			serverSocket = new ServerSocket(4444);
			System.out.println("Started Echo server on local port 4444");
		} 
		catch (IOException e)
		{
			// NOTE only one server / application (Java or not) bind to and listen on a port at one time
			// If this exception is thrown it means the port is bound by another program, probably your
			// program that didn't shut down correctly. Kill it from the eclipse console, or 
			// from the task manager kill the process "java".
			System.out.println("Could not listen on port 4444, check that it is not already in use.");
			System.exit(-1);
		}
		try 
		{
			clientSocket = serverSocket.accept();
			System.out.println("Accepted client from: " + clientSocket.getInetAddress());
		} 
		catch (IOException e) 
		{
			// if there is a generic IO error setting up the connection
			System.out.println("Accept of client failed, quitting.");
			System.exit(1);
		}
		
		// start thread to read messages from remote and display
		new Thread (new ReadFromRemote(clientSocket, this)).start();
		
		// start thread to read messages from console and send to remote
		// I need to make this a daemon thread (look it up) as I have no way
		// to stop it explicitly (closing the keyboard socket does not work)
		// Test it to see the difference : Set daemon to false and see if your 
		// server program ever ends. If you find an alternative, let me know.
		Thread t = new Thread (new ReadFromConsole (clientSocket, this));
		t.setDaemon(true);
		t.start();
	}

	@Override
	public void quit() 
	{
		// use a flag to ensure that the quit logic executes only once. This is likely since
		// quit is triggered both when the message "bye." is received AND the remote connection is closed.
		if (isActive)
		{
			System.out.println ("Quitting server");
			isActive = false;
			try
			{
				clientSocket.close();
				serverSocket.close();
			} 
			catch (IOException e) 
			{
				// ignore errors on close
				e.printStackTrace();
			}
		}
	}
}

